package cn.cdqf.coupon.dispatch.service.impl;

import cn.cdqf.coupon.common.CommonConstant;
import cn.cdqf.coupon.dispatch.client.CouponTemplateControllerApi;
import cn.cdqf.coupon.dispatch.client.RabbitControllerApi;
import cn.cdqf.coupon.dispatch.dao.CouponCodeUserMapper;
import cn.cdqf.coupon.dispatch.service.DispatchService;
import cn.cdqf.coupon.util.FutureUtils;
import cn.cdqf.coupon.util.IdUtils;
import cn.cdqf.globle.common.ResultEnum;
import cn.cdqf.globle.common.ResultResponse;
import cn.cdqf.globle.param.RabbitMqParam;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

import static cn.cdqf.coupon.common.CommonConstant.UserCodeCouponId.REDIS_USER_AND_COUPONID_FOR_CODE;
import static cn.cdqf.coupon.common.CommonConstant.UserCodeCouponId.REDIS_USER_FOR_CODE;

@Service
public class DispatchServiceImpl implements DispatchService {
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    @SuppressWarnings("all")
    private CouponCodeUserMapper couponCodeUserMapper;
    @Autowired
    private CouponTemplateControllerApi couponTemplateControllerApi;
    @Override
    @Transactional
    public ResultResponse dispatchByUserIdAndLevelAndMerchantId(String merchantId, String userId, Integer level) {
        if(StringUtils.isBlank(merchantId)||StringUtils.isBlank(userId)){
            return ResultResponse.fail(ResultEnum.PARAM_ERROR.getMessage());
        }
        level =  level ==null||level<1?1:level;

        ResultResponse couponTemplateByMAndLevel = couponTemplateControllerApi.getCouponTemplateMapByMAndLevel(merchantId, level);
        if(!couponTemplateByMAndLevel.isSuccess()) {
            return ResultResponse.fail("没有");
        }
        //拿到了当前 商家+level所有优惠券模板
        Map<String,Integer>  maps = (Map) couponTemplateByMAndLevel.getData();
        //只要当前用户领取了优惠券码 就会放在redis  set集合
        Optional<String> first = maps.keySet().stream().filter(couponId -> {
            Long size = redisUtils.sgetSize(REDIS_USER_AND_COUPONID_FOR_CODE + userId + CommonConstant.REDIS_SEPARATOR + couponId);
            //如果没取到 select count(1) from c_coupon_code_user where userid=#{userId}
            return size < maps.get(couponId);
        }).findFirst();
        if(!first.isPresent()){
            return ResultResponse.fail("no 符合等级得模板id");
        }
        String couponId = first.get();
            //减库存       C  模板表 强一直性  update。。。。行级锁 写锁：互斥锁 couponId 造成事务阻塞
            // 1.优惠券为1 只会出现领不到
            //加入优惠券库存还多 只会出现领不到
            ResultResponse resultResponse = couponTemplateControllerApi.decrByCouponId(couponId,1);
            if(!resultResponse.isSuccess()){
                return ResultResponse.fail("没货了");
            }
        String code=null;
            //反向操作
            try{
            //根据模板id取优惠券码
            //有事务的方法必须通过代理对象调用

            ResultResponse codeByCouponId = dispatchService.getCodeByCouponId(couponId);
             code = (String)codeByCouponId.getData();
            //插入数据库
           int i =  couponCodeUserMapper.updateUserIdByCode(code,userId);
           if(i>0){
               //当前用户对应的优惠券码
               redisUtils.sset(REDIS_USER_FOR_CODE+userId,code);
               //当前用户+模板id  对应的优惠券码
               redisUtils.sset(REDIS_USER_AND_COUPONID_FOR_CODE+userId+CommonConstant.REDIS_SEPARATOR+couponId,code);
           }else{
               return ResultResponse.fail();
             }
               // return ResultResponse.success(couponId);
                throw new RuntimeException("自己定义的异常");
            }catch (Exception e){
                redisUtils.sRemove(REDIS_USER_FOR_CODE+userId,code);
                redisUtils.sRemove(REDIS_USER_AND_COUPONID_FOR_CODE+userId+CommonConstant.REDIS_SEPARATOR+couponId,code);
                //发个消息 让他反向操作  达到最终一致性  一个是模板id 一个扣减个数
                HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
                objectObjectHashMap.put("couponId",couponId);
                objectObjectHashMap.put("count",1);
                HashMap hashMap = new HashMap();
                hashMap.put("a","b");
                try {
                    RabbitMqParam build = RabbitMqParam.builder().exchange("dispatch_for_template").message(objectMapper.writeValueAsString(objectObjectHashMap))
                            .properties(hashMap).routKey("decrTemplate").build();
                    CompletableFuture.runAsync(()-> rabbitControllerApi.send(build));
                } catch (JsonProcessingException e1) {
                    e1.printStackTrace();
                }

                throw  new RuntimeException(e);
            }
        }
        @Autowired
        private ObjectMapper objectMapper;
    @Autowired
    private RabbitControllerApi rabbitControllerApi;
    @Autowired
    private DispatchService dispatchService;
    @Autowired
    private IdUtils idUtils;
    @Override
    @Transactional
    public ResultResponse getCodeByCouponId(String couponId) {
        if(StringUtils.isBlank(couponId)) return  ResultResponse.fail(ResultEnum.PARAM_ERROR.getMessage());
        String couponCode = (String)redisUtils.spop(CommonConstant.CouponConstant.REDIS_COUPON_FOR_CODE + couponId);
        if(couponCode==null){
            //走数据库查询
         List<String> codes =  couponCodeUserMapper.queryByCouponId(couponId);
         if(!CollectionUtils.isEmpty(codes)) {
             redisUtils.sset(CommonConstant.CouponConstant.REDIS_COUPON_FOR_CODE + couponId, codes.toArray());
             //放入redis 再取一个
             couponCode = (String)redisUtils.spop(CommonConstant.CouponConstant.REDIS_COUPON_FOR_CODE + couponId);
         }else{
             //库存有  但是没有优惠券码 应该生成优惠券码,放入数据库
             couponCode = FutureUtils.get(idUtils.getId(couponId));
             couponCodeUserMapper.insertCouponCodeByCouponId(couponId,couponCode);
         }
        }
        return ResultResponse.success(couponCode);
    }
}
